package com.xm.bluetoothkotlin.connect

import android.annotation.SuppressLint
import android.app.PendingIntent.getService
import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattDescriptor
import android.bluetooth.BluetoothGattService
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.os.Message
import com.xm.bluetoothkotlin.connect.back.*
import com.xm.bluetoothkotlin.constant.*
import java.util.*

/**
 * @创建者:小垚
 * @时间:2021/11/12
 *@描述: 蓝牙连接 和写入
 */
class BluetoothConnect(var bleBluetooth: BleBluetooth) {
    val DEFAULT_DESCRIPTOR_UUID = "00002902-0000-1000-8000-00805f9b34fb"

    private var mGattService: BluetoothGattService? = null
    private var mCharacteristic: BluetoothGattCharacteristic? = null

    private val mHandler by lazy {
        object : Handler(Looper.getMainLooper()) {
            override fun handleMessage(msg: Message) {
                with(msg) {
                    when (what) {
                        MSG_CHA_NOTIFY_START         -> {
                            //通知超时
                            val notifyCallback = obj as BleNotifyCallback
                            notifyCallback?.run {
                                onNotifyFailure("超时")
                            }
                        }

                        MSG_CHA_NOTIFY_RESULT        -> {
                            notifyMsgInit()
                            var notifyCallback = obj as BleNotifyCallback
                            var bundle = data
                            var status = bundle.getInt(KEY_NOTIFY_BUNDLE_STATUS)
                            notifyCallback?.run {
                                if (status == BluetoothGatt.GATT_SUCCESS) {
                                    onNotifySuccess()
                                } else {
                                    onNotifyFailure("status:${status}")
                                }
                            }
                        }

                        MSG_CHA_NOTIFY_DATA_CHANGE   -> {
                            var notifyCallback = obj as BleNotifyCallback
                            notifyCallback?.run {
                                onCharacteristicChanged(data.getByteArray(KEY_NOTIFY_BUNDLE_VALUE)!!, data.getString(KEY_NOTIFY_BUNDLE_UUID_VALUE)!!)
                            }
                        }

                        MSG_CHA_INDICATE_START       -> {
                            var indicateCallback = obj as BleIndicateCallback
                            indicateCallback?.run {
                                onIndicateFailure("超时")
                            }
                        }

                        MSG_CHA_INDICATE_RESULT      -> {
                            indicateMsgInit()
                            var indicateCallback = obj as BleIndicateCallback
                            indicateCallback?.run {
                                if (data.getInt(KEY_INDICATE_BUNDLE_STATUS) == BluetoothGatt.GATT_SUCCESS) {
                                    onIndicateSuccess()
                                } else {
                                    onIndicateFailure("status:${data.getInt(KEY_INDICATE_BUNDLE_STATUS)}")
                                }
                            }
                        }

                        MSG_CHA_INDICATE_DATA_CHANGE -> {
                            var indicateCallback = obj as BleIndicateCallback
                            indicateCallback?.run {
                                onCharacteristicChanged(data.getByteArray(KEY_INDICATE_BUNDLE_VALUE)!!)
                            }
                        }

                        MSG_CHA_WRITE_START          -> {
                            var writeCallback = obj as BleWriteCallback
                            writeCallback?.run {
                                onWriteFailure("超时")
                            }
                        }

                        MSG_CHA_WRITE_RESULT         -> {
                            writeMsgInit()

                            var writeCallback = obj as BleWriteCallback
                            writeCallback?.run {
                                if (data.getInt(KEY_WRITE_BUNDLE_STATUS) == BluetoothGatt.GATT_SUCCESS) {
                                    onWriteSuccess(1, 1, data.getByteArray(KEY_WRITE_BUNDLE_VALUE)!!)
                                } else {
                                    onWriteFailure("status:${data.getInt(KEY_WRITE_BUNDLE_STATUS)}")
                                }
                            }
                        }

                        MSG_CHA_READ_START           -> {
                            var readCallback = obj as BleReadCallback
                            readCallback?.run {
                                onReadFailure("超时")
                            }
                        }

                        MSG_CHA_READ_RESULT          -> {
                            readMsgInit()

                            var readCallback = obj as BleReadCallback
                            readCallback?.run {
                                if (data.getInt(KEY_READ_BUNDLE_STATUS) == BluetoothGatt.GATT_SUCCESS) {
                                    onReadSuccess(data.getByteArray(KEY_READ_BUNDLE_VALUE)!!)
                                } else {
                                    onReadFailure("status:${data.getInt(KEY_READ_BUNDLE_STATUS)}")
                                }
                            }
                        }

                        MSG_READ_RSSI_START          -> {
                            var rssiCallback = obj as BleRssiCallback
                            rssiCallback?.run {
                                onRssiFailure("超时")
                            }
                        }

                        MSG_READ_RSSI_RESULT         -> {
                            rssiMsgInit()

                            var rssiCallback = obj as BleRssiCallback
                            rssiCallback?.run {
                                if (data.getInt(KEY_READ_RSSI_BUNDLE_STATUS) == BluetoothGatt.GATT_SUCCESS) {
                                    onRssiSuccess(data.getInt(KEY_READ_RSSI_BUNDLE_VALUE))
                                } else {
                                    onRssiFailure("status:${data.getInt(KEY_READ_RSSI_BUNDLE_STATUS)}")
                                }
                            }
                        }

                        MSG_SET_MTU_START            -> {
                            var mtuChangedCallback = obj as BleMtuCallback
                            mtuChangedCallback?.run {
                                onSetMTUFailure("超时")
                            }
                        }

                        MSG_SET_MTU_RESULT           -> {
                            mtuChangedMsgInit()

                            var mtuChangedCallback = obj as BleMtuCallback
                            mtuChangedCallback?.run {
                                if (data.getInt(KEY_SET_MTU_BUNDLE_STATUS) == BluetoothGatt.GATT_SUCCESS) {
                                    onMtuChanged(data.getInt(KEY_SET_MTU_BUNDLE_VALUE))
                                } else {
                                    onSetMTUFailure("status:${data.getInt(KEY_SET_MTU_BUNDLE_STATUS)}")
                                }
                            }
                        }
                        else                         -> {}
                    }
                }
            }
        }
    }

    private fun withUUID(serviceUUID: UUID, characteristicUUID: UUID): BluetoothConnect {
        bleBluetooth.bluetoothGatt?.run {
            mGattService = getService(serviceUUID)
        }

        mGattService?.run {
            mCharacteristic = getCharacteristic(characteristicUUID)
        }
        return this
    }

    fun withUUIDString(serviceUUID: String, characteristicUUID: String): BluetoothConnect {
        return withUUID(UUID.fromString(serviceUUID), UUID.fromString(characteristicUUID))
    }


    fun enableCharacteristicNotify(bleNotifyCallback: BleNotifyCallback, uuid_notify: String, userCharacteristicDescriptor: Boolean) {
        if (mCharacteristic != null
            && mCharacteristic!!.properties or BluetoothGattCharacteristic.PROPERTY_NOTIFY > 0
        ) {
            handleCharacteristicNotifyCallback(bleNotifyCallback, uuid_notify)
            setCharacteristicNotification(bleBluetooth.bluetoothGatt, mCharacteristic, userCharacteristicDescriptor, true, bleNotifyCallback)
        } else {
            bleNotifyCallback.onNotifyFailure("通知错误")
        }
    }

    fun disableCharacteristicNotify(useCharacteristicDescriptor: Boolean): Boolean {
        return if (mCharacteristic != null
            && mCharacteristic!!.properties or BluetoothGattCharacteristic.PROPERTY_NOTIFY > 0
        ) {
            setCharacteristicNotification(
                bleBluetooth.bluetoothGatt, mCharacteristic,
                useCharacteristicDescriptor, false, null
            )
        } else {
            false
        }
    }


    @SuppressLint("MissingPermission")
    private fun setCharacteristicNotification(
        gatt: BluetoothGatt?,
        characteristic: BluetoothGattCharacteristic?,
        useCharacteristicDescriptor: Boolean,
        enable: Boolean,
        bleNotifyCallback: BleNotifyCallback?
    ): Boolean {
        if (gatt == null || characteristic == null) {
            notifyMsgInit()
            bleNotifyCallback?.onNotifyFailure("gatt or characteristic == null")
            return false
        }
        val success1 = gatt.setCharacteristicNotification(characteristic, enable)
        if (!success1) {
            notifyMsgInit()
            bleNotifyCallback?.onNotifyFailure("gatt setCharacteristicNotification 错误")
            return false
        }

        var descriptor = if (useCharacteristicDescriptor) {
            characteristic.getDescriptor(characteristic.uuid)
        } else {
            characteristic.getDescriptor(UUID.fromString(DEFAULT_DESCRIPTOR_UUID))
        }

        if (descriptor == null) {
            notifyMsgInit()
            bleNotifyCallback?.onNotifyFailure("descriptor == null")
            return false
        } else {
            descriptor.value = if (enable) BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE else BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE
            val success2 = gatt.writeDescriptor(descriptor)
            if (!success2) {
                notifyMsgInit()
                bleNotifyCallback?.onNotifyFailure("gatt writeDescriptor fail")
                return false
            }
            return success2
        }
    }


    fun enableCharacteristicIndicate(bleIndicateCallback: BleIndicateCallback, uuid_indicate: String, useCharacteristicDescriptor: Boolean) {
        if (mCharacteristic != null
            && mCharacteristic!!.properties or BluetoothGattCharacteristic.PROPERTY_NOTIFY > 0
        ) {
            handleCharacteristicIndicateCallback(bleIndicateCallback, uuid_indicate)
            setCharacteristicIndication(
                bleBluetooth.bluetoothGatt, mCharacteristic,
                useCharacteristicDescriptor, true, bleIndicateCallback
            )
        } else {
            bleIndicateCallback.onIndicateFailure("this characteristic not support indicate!")
        }
    }

    fun disableCharacteristicIndicate(userCharacteristicDescriptor: Boolean): Boolean {
        return if (mCharacteristic != null
            && mCharacteristic!!.properties or BluetoothGattCharacteristic.PROPERTY_NOTIFY > 0
        ) {
            setCharacteristicIndication(
                bleBluetooth.bluetoothGatt, mCharacteristic,
                userCharacteristicDescriptor, false, null
            )
        } else {
            false
        }
    }


    @SuppressLint("MissingPermission")
    private fun setCharacteristicIndication(
        gatt: BluetoothGatt?,
        characteristic: BluetoothGattCharacteristic?,
        useCharacteristicDescriptor: Boolean,
        enable: Boolean,
        bleIndicateCallback: BleIndicateCallback?
    ): Boolean {
        if (gatt == null || characteristic == null) {
            indicateMsgInit()
            bleIndicateCallback?.onIndicateFailure("gatt or characteristic equal null")
            return false
        }
        val success1 = gatt.setCharacteristicNotification(characteristic, enable)
        if (!success1) {
            indicateMsgInit()
            bleIndicateCallback?.onIndicateFailure("gatt setCharacteristicNotification fail")
            return false
        }

        val descriptor = if (useCharacteristicDescriptor) {
            characteristic.getDescriptor(characteristic.uuid)
        } else {
            characteristic.getDescriptor(UUID.fromString(DEFAULT_DESCRIPTOR_UUID))
        }

        if (descriptor == null) {
            indicateMsgInit()
            bleIndicateCallback?.onIndicateFailure("descriptor equals null")
            return false
        } else {
            descriptor.value = if (enable) BluetoothGattDescriptor.ENABLE_INDICATION_VALUE else BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE
            val success2 = gatt.writeDescriptor(descriptor)
            if (!success2) {
                indicateMsgInit()
                bleIndicateCallback?.onIndicateFailure("gatt writeDescriptor fail")
            }
            return success2
        }
    }

    @SuppressLint("MissingPermission")
    fun writeCharacteristic(data: ByteArray?, bleWriteCallback: BleWriteCallback?, uuid_write: String) {
        if (data == null || data.isEmpty()) {
            bleWriteCallback?.onWriteFailure("the data to be written is empty")
            return
        }

        if (mCharacteristic == null
            || mCharacteristic!!.properties and (BluetoothGattCharacteristic.PROPERTY_WRITE or BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0
        ) {
            bleWriteCallback?.onWriteFailure("this characteristic not support write!")
            return
        }

        if (mCharacteristic!!.setValue(data)) {
            handleCharacteristicWriteCallback(bleWriteCallback, uuid_write)
            if (!bleBluetooth.bluetoothGatt!!.writeCharacteristic(mCharacteristic)) {
                writeMsgInit()
                bleWriteCallback?.onWriteFailure("gatt writeCharacteristic fail")
            }
        } else {
            bleWriteCallback?.onWriteFailure("Updates the locally stored value of this characteristic fail")
        }
    }

    /**
     * read
     */
    @SuppressLint("MissingPermission")
    fun readCharacteristic(bleReadCallback: BleReadCallback?, uuid_read: String?) {
        if (mCharacteristic != null
            && mCharacteristic!!.properties and BluetoothGattCharacteristic.PROPERTY_READ > 0
        ) {
            handleCharacteristicReadCallback(bleReadCallback, uuid_read!!)
            if (!bleBluetooth.bluetoothGatt!!.readCharacteristic(mCharacteristic)) {
                readMsgInit()
                bleReadCallback?.onReadFailure("gatt readCharacteristic fail")
            }
        } else {
            bleReadCallback?.onReadFailure("this characteristic not support read!")
        }
    }

    /**
     * rssi
     */
    @SuppressLint("MissingPermission")
    fun readRemoteRssi(bleRssiCallback: BleRssiCallback?) {
        handleRSSIReadCallback(bleRssiCallback)
        if (!bleBluetooth.bluetoothGatt!!.readRemoteRssi()) {
            rssiMsgInit()
            bleRssiCallback?.onRssiFailure("gatt readRemoteRssi fail")
        }
    }

    /**
     * set mtu
     */
    @SuppressLint("MissingPermission")
    fun setMtu(requiredMtu: Int, bleMtuChangedCallback: BleMtuCallback?) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            handleSetMtuCallback(bleMtuChangedCallback)
            if (!bleBluetooth.bluetoothGatt!!.requestMtu(requiredMtu)) {
                mtuChangedMsgInit()
                bleMtuChangedCallback?.onSetMTUFailure("gatt requestMtu fail")
            }
        } else {
            bleMtuChangedCallback?.onSetMTUFailure("API level lower than 21")
        }
    }

    /**
     * requestConnectionPriority
     *
     * @param connectionPriority Request a specific connection priority. Must be one of
     * [BluetoothGatt.CONNECTION_PRIORITY_BALANCED],
     * [BluetoothGatt.CONNECTION_PRIORITY_HIGH]
     * or [BluetoothGatt.CONNECTION_PRIORITY_LOW_POWER].
     * @throws IllegalArgumentException If the parameters are outside of their
     * specified range.
     */
    @SuppressLint("MissingPermission")
    fun requestConnectionPriority(connectionPriority: Int): Boolean {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            bleBluetooth.bluetoothGatt!!.requestConnectionPriority(connectionPriority)
        } else false
    }

    /**************************************** Handle call back ******************************************/


    private fun handleCharacteristicNotifyCallback(bleNotifyCallback: BleNotifyCallback?, uuid_notify: String) {
        bleNotifyCallback?.run {
            notifyMsgInit()
            bleNotifyCallback.key = uuid_notify
            bleNotifyCallback.handler = mHandler
            bleBluetooth.addNotifyCallback(uuid_notify, bleNotifyCallback)
            mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CHA_NOTIFY_START, bleNotifyCallback), DEFAULT_OPERATE_TIME)
        }
    }

    private fun handleCharacteristicIndicateCallback(bleIndicateCallback: BleIndicateCallback?, uuidIndicate: String) {
        bleIndicateCallback?.run {
            indicateMsgInit()
            bleIndicateCallback.key = uuidIndicate
            bleIndicateCallback.handler = mHandler
            bleBluetooth.addIndicateCallback(uuidIndicate, bleIndicateCallback)
            mHandler.sendMessageDelayed(
                mHandler.obtainMessage(MSG_CHA_INDICATE_START, bleIndicateCallback),
                DEFAULT_OPERATE_TIME
            )
        }
    }


    private fun handleCharacteristicWriteCallback(bleWriteCallback: BleWriteCallback?, uuidWrite: String) {
        bleWriteCallback?.run {
            writeMsgInit()
            key = uuidWrite
            handler = mHandler
            bleBluetooth.addWriteCallback(uuidWrite, bleWriteCallback);
            mHandler.sendMessageDelayed(
                mHandler.obtainMessage(MSG_CHA_WRITE_START, bleWriteCallback),
                DEFAULT_OPERATE_TIME
            )
        }
    }

    private fun handleCharacteristicReadCallback(bleReadCallback: BleReadCallback?, uuid_read: String) {
        bleReadCallback?.run {
            readMsgInit()
            key = uuid_read
            handler = mHandler
            bleBluetooth.addReadCallback(uuid_read, bleReadCallback);
            mHandler.sendMessageDelayed(
                mHandler.obtainMessage(MSG_CHA_READ_START, this),
                DEFAULT_OPERATE_TIME
            )
        }
    }

    private fun handleRSSIReadCallback(bleRssiCallback: BleRssiCallback?) {
        bleRssiCallback?.run {
            rssiMsgInit()
            handler = mHandler
            bleBluetooth.addRssiCallback(this);
            mHandler.sendMessageDelayed(
                mHandler.obtainMessage(MSG_READ_RSSI_START, this),
                DEFAULT_OPERATE_TIME
            )
        }
    }

    private fun handleSetMtuCallback(bleMtuChangedCallback: BleMtuCallback?) {
        bleMtuChangedCallback?.run {
            mtuChangedMsgInit()
            handler = mHandler
            bleBluetooth.addMtuChangedCallback(this);
            mHandler.sendMessageDelayed(
                mHandler.obtainMessage(MSG_SET_MTU_START, this),
                DEFAULT_OPERATE_TIME
            )
        }
    }


    fun notifyMsgInit() {
        mHandler.removeMessages(MSG_CHA_NOTIFY_START)
    }

    fun indicateMsgInit() {
        mHandler.removeMessages(MSG_CHA_INDICATE_START)
    }

    fun writeMsgInit() {
        mHandler.removeMessages(MSG_CHA_WRITE_START)
    }

    fun readMsgInit() {
        mHandler.removeMessages(MSG_CHA_READ_START)
    }

    fun rssiMsgInit() {
        mHandler.removeMessages(MSG_READ_RSSI_START)
    }

    fun mtuChangedMsgInit() {
        mHandler.removeMessages(MSG_SET_MTU_START)
    }
}